package dryven.model.binding.form;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import dryven.model.binding.fieldset.FieldSet;
import dryven.model.binding.proxy.Binding;
import dryven.request.controller.ControllerDescription;
import dryven.request.routing.Router;
import dryven.request.routing.RoutingService;
import dryven.util.crypt.AESDecryption;
import dryven.util.crypt.AESGeneratedKey;
import dryven.util.crypt.SHA1Hash;

public class FormSignatureDecoder {
	private Map<Class<?>, Integer> _primaryKeys;
	private Binding<FieldSet> _fieldsetBinding;
	private AESGeneratedKey _key;
	private String _signSecret;
	private String _signature;
	private boolean _decoded;
	private String _errorActionURL;
	
	public FormSignatureDecoder(String signature, AESGeneratedKey aesKey, String signSecret) {
		_key = aesKey;
		_signSecret = signSecret;
		_signature = signature;
		_primaryKeys = new HashMap<Class<?>, Integer>();
		_decoded = false;
	}
	
	public String getErrorActionURL() {
		internalDecode();
		return _errorActionURL;
	}

	public Binding<FieldSet> getFieldsetBinding() {
		internalDecode();
		return _fieldsetBinding;
	}
	
	/**
	 * Returns the primary key of the object being edited in the form
	 * with this signature and the given type, or 0 if the object was not yet
	 * persisted.
	 * @param modelType
	 * @return the primary key, or 0 if the model was not yet persisted
	 */
	public int getPrimaryKeyForModel(Class<?> modelType) {
		internalDecode();
		return _primaryKeys.get(modelType);
	}
	
	public Iterable<Class<?>> getModelClasses() {
		internalDecode();
		return _primaryKeys.keySet();
	}
	
	private void internalDecode() {
		if(_decoded) {
			return;
		}
		_decoded = true;
		try {
			String decryptedSig = new AESDecryption(_key, _signature).decrypt();
			FormSignature sig = new FormSignature();
			sig.read(decryptedSig, _signSecret);
			//get model classes with primary keys
			for (String modelValue : sig.getValuesForType(FormSignature.ModelType)) {
				String modelParts[] = modelValue.split(":");
				if(modelParts.length!=2) {
					throw new RuntimeException("illegal model value");
				}
				String className = modelParts[0];
				String pkText = modelParts[1];
				Class<?> modelClass = Class.forName(className);
				int pk = Integer.parseInt(pkText);
				_primaryKeys.put(modelClass, pk);
			}
			//get binding
			String bindingText = sig.getFirstValueForType(FormSignature.FieldSetType);
			if(bindingText==null) {
				throw new RuntimeException("Binding missing from signature");
			}
			_fieldsetBinding = new Binding<FieldSet>(bindingText);
			//get error action url
			_errorActionURL = sig.getFirstValueForType(FormSignature.ErrorActionType);
			if(_errorActionURL==null) {
				throw new RuntimeException("Binding error action from signature");
			}
		} catch(Exception e) {
			throw new RuntimeException("It seems like there has been tampered with the request variables, aborting form decoding", e);
		}
	}
}
